當我們搞定了昨天的基本按鈕後,今天來動手實作網頁中超級重要的文字輸入框吧!
這些輸入框廣泛應用在註冊、登錄、搜尋、提交表單等等地方!
一開始先想想輸入框有哪些常見的狀態和類型🤔✨
例如:
disabled
當輸入框為不可輸入的狀態label
標題 與 placeholder
提示使用者輸入資訊step 1: 新增一個 input.vue
到 pages 資料夾下
step 2: 一樣先透過 interface
先定義 props
會傳入的類型,然後透過 withDefaults 給予預設值
// components/global/input.vue
<script setup lang="ts">
export interface TextInputPropsType {
/** 預設文字 */
placeholder?: string
/** 欄位名稱 */
label?: string
/** 自訂義欄位名稱 class */
classLabel?: string
/** 自訂義欄位 class */
classInput?: string
/** input 類型 */
type?: string
/** name 名稱連動 for 與 id */
name?: string
/** 響應式 value */
modelValue: any
/** 錯誤訊息 (可與成功訊息並存) */
error?: string
/** 是否禁用 */
disabled?: boolean
}
const props = withDefaults(defineProps<TextInputPropsType>(), {
placeholder: ' ',
label: undefined,
classLabel: ' ',
classInput: '',
type: 'text',
name: undefined,
error: undefined,
description: ''
})
defineEmits(['update:modelValue'])
const types = ref(props.type)
</script>
以往在使用 v-model
做父子層資料的雙向綁定總是要 props
+ emit
來進行傳遞,但在 Vue 3.4 版以後出現了一個大糖 defineModel
!
有了他就可以省略掉 props
+ emit
的過程~整個程式碼看起來更乾淨直觀了
// components/global/input.vue
<script setup lang="ts">
export interface TextInputPropsType {
/** 預設文字 */
placeholder?: string
/** 欄位名稱 */
label?: string
/** 自訂義欄位名稱 class */
classLabel?: string
/** 自訂義欄位 class */
classInput?: string
/** input 類型 */
type?: string
/** name 名稱連動 for 與 id */
name?: string
/** 錯誤訊息 (可與成功訊息並存) */
error?: string
/** 是否禁用 */
disabled?: boolean
}
const modelValue = defineModel<string>('modelValue', {required: true})
const props = withDefaults(defineProps<TextInputPropsType>(), {
placeholder: ' ',
label: undefined,
classLabel: ' ',
classInput: '',
type: 'text',
name: undefined,
error: undefined,
description: ''
})
const types = ref(props.type)
</script>
// components/global/input.vue
<template>
<div
class="fromGroup"
:class="{'has-error': error}"
>
<label
v-if="label"
class="flex input-label"
:for="name"
>
<span :class="classLabel">{{ label }}</span>
</label>
<div class="relative">
<input
:id="name"
:type="types"
:name="name"
:placeholder="placeholder"
:class="`${classInput} input-control`"
:value="modelValue"
:error="error"
:disabled="disabled"
@input="$emit('update:modelValue', ($event.target as HTMLInputElement)?.value)"
>
<div
class="absolute top-1/2 flex -translate-y-1/2 text-xl right-[14px]"
>
<span
v-if="error"
class="text-red-500"
>
<Icon name="heroicons-outline:information-circle" />
</span>
</div>
</div>
<span
v-if="error"
class="absolute -bottom-6 right-0 mt-2 text-red-500 block text-sm"
>{{ error }}</span>
</div>
</template>
<style>
.form-control {
@apply ...;
}
.form-label {
@apply ...
}
.input-control {
@apply ...
}
.input-label {
@apply ...;
}
.fromGroup.has-error .input-control {
@apply ...;
}
.input-control[disabled] {
@apply ...
}
</style>
完成之後的樣子是這樣
// pages/input.vue
<div class="flex gap-4">
<Input
v-model="inputValue"
label="姓名"
name="name"
placeholder="請輸入姓名"
/>
<Input
v-model="inputValueDisabled"
label="品種"
name="name"
disabled
placeholder="請輸入品種名"
/>
<Input
v-model.number="inputPhone"
label="電話"
name="phone"
type="text"
placeholder="請輸入電話"
error="無效的電話格式"
/>
</div>